#ifdef __cplusplus
#define __STDC_CONSTANT_MACROS
#ifdef _STDINT_H
#undef _STDINT_H
#endif
# include <stdint.h>
#endif
extern "C" {
	#include <libavcodec/avcodec.h>
	#include <libavformat/avformat.h>
}

#include <iostream>
#include <fstream>

using namespace std;

#define MAX_SIZE 1024

void register_codecs() {
	av_register_all(); //register the codecs on the system.
}

int getSize() {
	return (AVCODEC_MAX_AUDIO_FRAME_SIZE * 15);
}

int audio(const char * filename, const char * destination_file) {
	AVCodecContext *pCodecContext = NULL;
	AVFormatContext *pCtx = NULL;
	AVCodecContext *pEncoderContext = NULL;
	int audioStream = 0;
	AVPacket * packet = new AVPacket();
	ofstream arch(destination_file);
	if (filename != NULL) {
		register_codecs(); //registramos los codecs.
		//abrimos el archivo.
		if (av_open_input_file(&pCtx, filename, NULL, 0, NULL) < 0) {
			cerr << "Err en av_open_input_file" << endl;
		} else {
			if (av_find_stream_info(pCtx) < 0) {
				printf("%s", "Error en av_find_stream_info\n");
			} else {
				dump_format(pCtx, 0, filename, 0);
				int nb_streams = pCtx->nb_streams;
				for (int rest = 0; rest < nb_streams; rest++) {
					if (pCtx->streams[rest]->codec->codec_type
							== CODEC_TYPE_AUDIO) {
						audioStream = rest;
					}
				}
				pCodecContext = pCtx->streams[audioStream]->codec;
				AVCodec * presultCodec = avcodec_find_decoder(pCodecContext->codec_id); //creamos el decodificador.
				if(!presultCodec) {
					cerr << "Encoder no encontrado" << endl;
					return -1;
				}
				AVCodec *presultCodecEncoder;
				pEncoderContext = avcodec_alloc_context();
				if (pEncoderContext != NULL) {
					presultCodecEncoder = avcodec_find_encoder(CODEC_ID_MP2);//creamos el codificador.
				}
				else {
					return -1;
				}
				if (!presultCodecEncoder) {
					cerr << "Codec no encontrado" << endl;
					return -1;
				}
				pEncoderContext->sample_rate = 44100;
				pEncoderContext->sample_fmt = pCodecContext->sample_fmt;
				pEncoderContext->bit_rate = 160000;
				pEncoderContext->channels = pCodecContext->channels;
				cout << pCodecContext->sample_fmt << endl;
				if (avcodec_open(pEncoderContext, presultCodecEncoder) < 0) {
					cerr << "Error abriendo codificador" << endl;
					return -1;
				}
				if (avcodec_open(pCodecContext, presultCodec) < 0) {
					cerr << "Error abriendo decodificador" << endl;
					return -1;
				}
				else {
					//av_init_packet(packet); //init packet.
					uint8_t samples[AVCODEC_MAX_AUDIO_FRAME_SIZE];
					uint8_t out[AVCODEC_MAX_AUDIO_FRAME_SIZE];
					while (av_read_frame(pCtx, packet) >= 0) {
						int sampleSize = getSize();
						if (packet->stream_index == audioStream) {
							int ret = avcodec_decode_audio3(pCodecContext,
									(int16_t*) samples, &sampleSize, packet);
							if (ret > 0) {
								int p = avcodec_encode_audio(pEncoderContext,
										out, AVCODEC_MAX_AUDIO_FRAME_SIZE, (short*) samples);
								if (p > 0) {
									arch.write((char*)out, p);
								}
							}
						}
						av_free_packet(packet);
					}
					avcodec_close(pEncoderContext);
					avcodec_close(pCodecContext);
					av_free(pEncoderContext);
					arch.close();
				}
			}
		}
	}
	delete(packet);
	av_close_input_file(pCtx);
	return 0;
}

int main(int argc, char *argv[]) {
	string in;
	if ((argc - 1) == 0) {
		in = "john.mp2";
	}
	if ((argc - 1) == 1) {
		in = argv[1];
	}
	string out = "salida.mp2";
	audio(in.c_str(), out.c_str());
	cout << "end code" << endl;
	return 0;
}
